﻿using System.Collections.Generic;

using SwyishChess.Domain.Board;
using SwyishChess.Domain.Move;
using SwyishChess.Domain.Piece;
using SwyishChess.Domain.Player;

namespace SwyishChess.Service
{
    public class MoveTrackingService : IMoveTrakingService
    {
        private const int PLAYER_ONE = 0;
        private const int PLAYER_TWO = 1;
        private const int WHITE_PLAYER = 1;
        private const int BLACK_PLAYER = 2;

        /// <summary>
        /// Records the chess piece movement
        /// </summary>
        /// <param name="MoveTracker">List array which keep track of chess moves</param>
        /// <param name="cp">The chess piece that has been moved</param>
        /// <param name="currentRow">The row location which the chess piece was at originally</param>
        /// <param name="currentColumn">The column locatino which the chess piece was at originally</param>
        /// <param name="newRow">The new row location which the chess piece has moved to</param>
        /// <param name="newColumn">THe new column locatino which the chess piece has moved to </param>
        public void RecordMove(List<ChessMove> moveTracker, ChessPiece cp, int currentRow, int currentColumn,
            int newRow, int newColumn, ChessPiece capturedPiece)
        {
            cp.MoveCount++;
            moveTracker.Add(new ChessMove(cp, currentRow, currentColumn, newRow, newColumn, capturedPiece));
        }

        /// <summary>
        /// Overloaded RecordMove method
        /// </summary>
        /// <param name="MoveTracker"></param>
        /// <param name="cp"></param>
        /// <param name="currentRow"></param>
        /// <param name="currentColumn"></param>
        /// <param name="newRow"></param>
        /// <param name="newColumn"></param>
        /// <param name="capturedPiece"></param>
        /// <param name="specialMove"></param>
        public void RecordMove(List<ChessMove> moveTracker, ChessPiece cp, int currentRow, int currentColumn,
            int newRow, int newColumn, ChessPiece capturedPiece, SpecialMove specialMove)
        {
            cp.MoveCount++;
            moveTracker.Add(new SpecialChessMove(cp, currentRow, currentColumn, newRow, newColumn, capturedPiece, specialMove));
        }

        /// <summary>
        /// Undos a move
        /// </summary>
        /// <param name="MoveTracker">The list array which keeps track of chess moves</param>
        /// <param name="UndoedMoveTracker">The list array which keep track of undoed moves for redo</param> 
        /// <param name="ChessPieces">The chess pieces array</param>
        /// <param name="MoveID">index of the last move ID</param>
        public void UndoMove(List<ChessMove> moveTracker, List<ChessMove> undoedMoveTracker, ChessPiece[,] chessPieces, int moveID)
        {
            ChessPiece cp = moveTracker[moveID].ChessPiece;
            ChessPiece capturedPiece = moveTracker[moveID].CapturedPiece;
            cp.MoveCount--;

            chessPieces[moveTracker[moveID].CurrentRow, moveTracker[moveID].CurrentColumn] = cp;

            if (moveTracker[moveID] is SpecialChessMove)
            {
                UndoSpecialMove(moveTracker, chessPieces, moveID, capturedPiece);
            }
            else
            {
                chessPieces[moveTracker[moveID].NewRow, moveTracker[moveID].NewColumn] = capturedPiece;
            }

            undoedMoveTracker.Add(moveTracker[moveID]);
            moveTracker.RemoveAt(moveID);
        }

        /// <summary>
        /// Call undo methods based on the special move type
        /// </summary>
        /// <param name="MoveTracker"></param>
        /// <param name="ChessPieces"></param>
        /// <param name="MoveID"></param>
        /// <param name="capturedPiece"></param>
        private void UndoSpecialMove(List<ChessMove> moveTracker, ChessPiece[,] chessPieces,
            int moveID, ChessPiece capturedPiece)
        {
            if (((SpecialChessMove)moveTracker[moveID]).SpecialMoveType == SpecialMove.EnPassant)
            {
                UndoEnPasasnt(moveTracker, chessPieces, moveID, capturedPiece);
            }
            else if (((SpecialChessMove)moveTracker[moveID]).SpecialMoveType == SpecialMove.Castling)
            {
                UndoCastling(moveTracker, chessPieces, moveID);
            }
            chessPieces[moveTracker[moveID].NewRow, moveTracker[moveID].NewColumn] = null;
        }

        /// <summary>
        /// Undo En Passant
        /// </summary>
        /// <param name="MoveTracker"></param>
        /// <param name="ChessPieces"></param>
        /// <param name="MoveID"></param>
        /// <param name="capturedPiece"></param>
        private void UndoEnPasasnt(List<ChessMove> moveTracker, ChessPiece[,] chessPieces, int moveID, ChessPiece capturedPiece)
        {
            if (((SpecialChessMove)moveTracker[moveID]).OwningPlayerID == WHITE_PLAYER)
            {
                chessPieces[moveTracker[moveID].NewRow + 1, moveTracker[moveID].NewColumn] = capturedPiece;
            }
            else
            {
                chessPieces[moveTracker[moveID].NewRow - 1, moveTracker[moveID].NewColumn] = capturedPiece;
            }
            ((Pawn)capturedPiece).WasLastMove = true;
        }

        /// <summary>
        /// Undo Castling
        /// </summary>
        /// <param name="MoveTracker"></param>
        /// <param name="ChessPieces"></param>
        /// <param name="MoveID"></param>
        private void UndoCastling(List<ChessMove> moveTracker, ChessPiece[,] chessPieces, int moveID)
        {
            ChessPiece affectedPiece = ((SpecialChessMove)moveTracker[moveID]).AffectedChessPiece;
            if (moveTracker[moveID].NewColumn == 2)
            {
                chessPieces[moveTracker[moveID].NewRow, 0] = affectedPiece;
                chessPieces[moveTracker[moveID].NewRow, 3] = null;
            }
            else
            {
                chessPieces[moveTracker[moveID].NewRow, 7] = affectedPiece;
                chessPieces[moveTracker[moveID].NewRow, 5] = null;
            }
        }

        /// <summary>
        /// Redos a move
        /// </summary>
        /// <param name="MoveTracker">The list array which keeps track of chess moves</param>
        /// <param name="UndoedMoveTracker">The list array which keep track of undoed moves for redo</param> 
        /// <param name="ChessPieces">The chess pieces array</param>
        /// <param name="MoveID">index of the last move ID</param>
        public void RedoMove(List<ChessMove> moveTracker, List<ChessMove> undoedMoveTracker, ChessPiece[,] chessPieces, int moveID)
        {
            ChessPiece capturedPiece = undoedMoveTracker[moveID].CapturedPiece;
            ChessPiece cp = undoedMoveTracker[moveID].ChessPiece;
            cp.MoveCount++;

            // In case of a special move
            if (undoedMoveTracker[moveID] is SpecialChessMove)
            {
                if (((SpecialChessMove)undoedMoveTracker[moveID]).SpecialMoveType == SpecialMove.EnPassant)
                {
                    RedoEnPassant(undoedMoveTracker, chessPieces, moveID, cp);

                    RecordMove(moveTracker, cp, undoedMoveTracker[moveID].CurrentRow, undoedMoveTracker[moveID].CurrentColumn,
                        undoedMoveTracker[moveID].NewRow, undoedMoveTracker[moveID].NewColumn, capturedPiece, SpecialMove.EnPassant);
                }
                else if (((SpecialChessMove)undoedMoveTracker[moveID]).SpecialMoveType == SpecialMove.Castling)
                {
                    ChessPiece affectedPiece = ReDoCastling(undoedMoveTracker, chessPieces, moveID, cp);

                    RecordMove(moveTracker, cp, undoedMoveTracker[moveID].CurrentRow, undoedMoveTracker[moveID].CurrentColumn,
                        undoedMoveTracker[moveID].NewRow, undoedMoveTracker[moveID].NewColumn, capturedPiece, SpecialMove.Castling);
                    ((SpecialChessMove)moveTracker[moveTracker.Count - 1]).AffectedChessPiece = affectedPiece;
                }
            }
            else
            {
                chessPieces[undoedMoveTracker[moveID].CurrentRow, undoedMoveTracker[moveID].CurrentColumn] = null;
                chessPieces[undoedMoveTracker[moveID].NewRow, undoedMoveTracker[moveID].NewColumn] = cp;
                RecordMove(moveTracker, cp, undoedMoveTracker[moveID].CurrentRow, undoedMoveTracker[moveID].CurrentColumn,
                    undoedMoveTracker[moveID].NewRow, undoedMoveTracker[moveID].NewColumn, capturedPiece);
            }

            undoedMoveTracker.RemoveAt(moveID);
        }

        /// <summary>
        /// Redoes castling
        /// </summary>
        /// <param name="undoedMoveTracker">Chess moves array which keeps track of undo moves</param>
        /// <param name="chessPieces">Chess pieces array</param>
        /// <param name="moveID">The index of the move in chess move array</param>
        /// <param name="cp">Castling causing piece</param>
        /// <returns>The affected chess piece</returns>
        private ChessPiece ReDoCastling(List<ChessMove> undoedMoveTracker, ChessPiece[,] chessPieces, int moveID, ChessPiece cp)
        {
            ChessPiece affectedPiece = ((SpecialChessMove)undoedMoveTracker[moveID]).AffectedChessPiece;
            chessPieces[undoedMoveTracker[moveID].CurrentRow, undoedMoveTracker[moveID].CurrentColumn] = null;
            chessPieces[undoedMoveTracker[moveID].NewRow, undoedMoveTracker[moveID].NewColumn] = cp;

            if (undoedMoveTracker[moveID].NewColumn == 2)
            {
                chessPieces[undoedMoveTracker[moveID].NewRow, 0] = null;
                chessPieces[undoedMoveTracker[moveID].NewRow, 3] = affectedPiece;
            }
            else
            {
                chessPieces[undoedMoveTracker[moveID].NewRow, 7] = null;
                chessPieces[undoedMoveTracker[moveID].NewRow, 5] = affectedPiece;
            }
            return affectedPiece;
        }

        /// <summary>
        /// Redoes En Passant
        /// </summary>
        /// <param name="UndoedMoveTracker">Undo chessmove list</param>
        /// <param name="ChessPieces">Chess Pieces array</param>
        /// <param name="MoveID">Index of the current chess move</param>
        /// <param name="cp">Chesspiece cuasing the chess move</param>
        private void RedoEnPassant(List<ChessMove> undoedMoveTracker, ChessPiece[,] chessPieces, int moveID, ChessPiece cp)
        {
            chessPieces[undoedMoveTracker[moveID].CurrentRow, undoedMoveTracker[moveID].CurrentColumn] = null;
            chessPieces[undoedMoveTracker[moveID].NewRow, undoedMoveTracker[moveID].NewColumn] = cp;

            if (((SpecialChessMove)undoedMoveTracker[moveID]).OwningPlayerID == 1)
            {
                undoedMoveTracker[moveID].CapturedPiece = chessPieces[undoedMoveTracker[moveID].NewRow + 1,
                    undoedMoveTracker[moveID].NewColumn];
                chessPieces[undoedMoveTracker[moveID].NewRow + 1, undoedMoveTracker[moveID].NewColumn] = null;
            }
            else
            {
                undoedMoveTracker[moveID].CapturedPiece = chessPieces[undoedMoveTracker[moveID].NewRow + 1,
                    undoedMoveTracker[moveID].NewColumn];
                chessPieces[undoedMoveTracker[moveID].NewRow - 1, undoedMoveTracker[moveID].NewColumn] = null;
            }
        }

        /// <summary>
        /// Add captured piece to to the undo move tracker
        /// </summary>
        /// <param name="moveTracker">ChessMoves Array</param>
        /// <param name="capturedPiece">Captured piece reference</param>
        public void AddCapturedPieceToListMove(List<ChessMove> moveTracker, ChessPiece capturedPiece)
        {
            moveTracker[moveTracker.Count - 1].CapturedPiece = capturedPiece;
        }

        /// <summary>
        /// Add chess pieces affected by a special move to the undo move tracker
        /// </summary>
        /// <param name="moveTracker">ChessMoves array</param>
        /// <param name="affectedPiece">Affectred piece reference</param>
        public void AddAffectedPieceToListMove(List<ChessMove> moveTracker, ChessPiece affectedPiece)
        {
            ((SpecialChessMove)moveTracker[moveTracker.Count - 1]).AffectedChessPiece = affectedPiece;
        }
    }
}
